package pl.edu.agh.intobl.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import pl.edu.agh.intobl.exceptions.BadArgumentException;
import pl.edu.agh.intobl.exceptions.BadFileFormatException;

/**
 * Utility class for reading and obtaining input data 
 */
public class InputParser {

	final Integer vehiclesNr;
	final Integer vehiclesCapacity;
	final Integer[] demands;
	final Integer[][] distanceMatrix;
	
	/**
	 * Default constructor. Parses input file and stores all parameters in fields
	 * 
	 * @param file input file filled with input data. Handled file format:
	 * 			<number of vehicles> <vehicles capacity> (both integers)
	 * 			<first demand> <second demand> ... <n-th demand>
	 * 			(n+1)x(n+1) distance matrix M (0 is the depot and 1..n are customers).
	 * 
	 * 			Matrix must be symmetric, so that M[i][j] == M[j][i]
	 * 			Distance from each point to itself equals 0, so that means
	 * 			that matrix must have zeros all over it's diagonal
	 * 
	 * @throws BadArgumentException thrown when passed argument is null or given file
	 * 			couldn't been found
	 * @throws BadFileFormatException thrown when file format constraints aren't abided
	 */
	public InputParser(File file) throws BadArgumentException, BadFileFormatException {
		if (file == null)
			throw new BadArgumentException("file cannot be null");
		
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new FileReader(file));
		} catch (FileNotFoundException e) {
			throw new BadArgumentException(e);
		}
		
		try {
			Integer[] vehiclesParsedData = parseVehicles(reader.readLine());
			vehiclesNr = vehiclesParsedData[0];
			vehiclesCapacity = vehiclesParsedData[1];
			
			Integer[] demandsParsedData = parseDemands(reader.readLine());
			demands = demandsParsedData;
			
			Integer[][] parsedDistanceMatrix = parseDistanceMatrix(reader);
			distanceMatrix = parsedDistanceMatrix;
		} catch (IOException e) {
			throw new BadFileFormatException("An IO exception occured during parsing input file");
		} catch (NumberFormatException e) {
			throw new BadFileFormatException("either vehicle capacity, number of vehicles or the distance from client " +
					"to client is not an integer");
		} 
		finally {
			try {
				reader.close();
			} catch (IOException e) {
				//whatever
			}
		}
	}
	
	/**
	 * @return vehiclesNr
	 */
	public int getVehiclesNumber() { 
		return vehiclesNr;
	}
	
	/**
	 * @return vehiclesCapacity
	 */
	public int getVehiclesCapacity() { 
		return vehiclesCapacity;
	}
	
	/**
	 * @return distanceMatrix
	 */
	public Integer[][] getDistanceMatrix() {
		return distanceMatrix;
	}
	
	/**
	 * @return demands
	 */
	public Integer[] getDemands() {
		return demands;
	}

	private Integer[] parseVehicles(String vehiclesDataLine) throws BadFileFormatException {
		String[] vehiclesData = vehiclesDataLine.split("\\s+");
		if (vehiclesData.length != 2)
			throw new BadFileFormatException("First line of input file has to be in following format: " +
					"<number of vehicles> <vehicles capacity> (both integers)");
		
		Integer[] vehiclesParsedData = new Integer[2];
		vehiclesParsedData[0] = Integer.parseInt(vehiclesData[0]);
		vehiclesParsedData[1] = Integer.parseInt(vehiclesData[1]);
		
		if (vehiclesParsedData[0] < 1)
			throw new BadFileFormatException("There needs to be at least one vehicle");
		if (vehiclesParsedData[1] < 1)
			throw new BadFileFormatException("Capacity of vehicles needs to be positive integer");
		
		return vehiclesParsedData;
	}

	private Integer[] parseDemands(String demandsDataLine) throws BadFileFormatException {
		String[] demandsData = demandsDataLine.split("\\s+");
		
		Integer[] parsedDemandsData = new Integer[demandsData.length];
		for (int i=0; i<demandsData.length; i++) {
			parsedDemandsData[i] = Integer.parseInt(demandsData[i]);
			if (parsedDemandsData[i] > vehiclesCapacity)
				throw new BadFileFormatException("Demand couldn't overgrow vehicle capacity");
		}
		
		if (demandsData.length < 1)
			throw new BadFileFormatException("There needs to be at least one demand");
		
		return parsedDemandsData;
	}

	private Integer[][] parseDistanceMatrix(BufferedReader reader) throws IOException, BadFileFormatException {
		String line = null;
		Integer[][] parsedMatrixData = new Integer[demands.length+1][demands.length+1];
		int rowNr = 0;
		while ((line = reader.readLine()) != null) {
			String[] row = line.split("\\s+");
			if (row.length != demands.length+1)
				throw new BadFileFormatException("Each row length has to be equal to demands number + 1");
			
			for (int i=0; i<row.length; i++)
				parsedMatrixData[rowNr][i] = Integer.parseInt(row[i]);
			
			rowNr++;
		}
		
		if (rowNr != demands.length+1)
			throw new BadFileFormatException("Row number has to be equal to demands number + 1");
		
		if (distanceConstraintsNotAbided(parsedMatrixData))
			throw new BadFileFormatException("There has to be zeros all over diagonal and matrix has to be simmetric");
		
		return parsedMatrixData;
	}

	private boolean distanceConstraintsNotAbided(Integer[][] parsedMatrixData) {
		for (int i=0;i<demands.length + 1; i++)
			if (parsedMatrixData[i][i] != 0)
				return true;
		
		for (int i=0;i<demands.length + 1; i++)
			for (int j=i+1;j<demands.length + 1; j++)
				if (parsedMatrixData[i][j] != parsedMatrixData[j][i])
					return true;
		
		return false;
	}
}
